home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Visual Cafe 3
/
Visual Cafe 3.ISO
/
Vcafe
/
JFC.bin
/
BasicSliderUI.java
< prev
next >
Wrap
Text File
|
1998-06-30
|
44KB
|
1,390 lines
/*
* @(#)BasicSliderUI.java 1.54 98/02/04
*
* Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the confidential and proprietary information of Sun
* Microsystems, Inc. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Sun.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THIS SOFTWARE OR ITS DERIVATIVES.
*
*/
package com.sun.java.swing.plaf.basic;
import java.awt.Component;
import java.awt.Container;
import java.awt.Adjustable;
import java.awt.event.AdjustmentListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.ComponentListener;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusListener;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Point;
import java.awt.Insets;
import java.awt.Color;
import java.io.Serializable;
import java.awt.IllegalComponentStateException;
import java.awt.Polygon;
import java.beans.*;
import java.util.*;
import com.sun.java.swing.border.AbstractBorder;
import com.sun.java.swing.*;
import com.sun.java.swing.event.*;
import com.sun.java.swing.plaf.*;
/**
* A Basic L&F implementation of SliderUI.
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*
* @version 1.54 02/04/98
* @author David Kloba
* @author Tom Santos
*/
public class BasicSliderUI extends SliderUI implements Serializable, PropertyChangeListener
{
// Public
public final int POSITIVE_SCROLL = +1;
public final int NEGATIVE_SCROLL = -1;
public final int MIN_SCROLL = -2;
public final int MAX_SCROLL = +2;
// Protected
protected ScrollListener scrollListener;
protected Timer scrollTimer;
protected JSlider slider;
protected Rectangle labelRect = new Rectangle( 0, 0, 0, 0 );
protected int trackBuffer = 0; // The distance that the track is from the side of the control
// Static private
private static final int TICK_SPACE = 8;
private static final Dimension PREFERRED_HORIZONTAL_SIZE = new Dimension(164, 21);
private static final Dimension PREFERRED_VERTICAL_SIZE = new Dimension(21, 164);
private static final Dimension MINIMUM_HORIZONTAL_SIZE = new Dimension(36, 21);
private static final Dimension MINIMUM_VERTICAL_SIZE = new Dimension(21, 36);
// Private
private transient boolean isDragging;
private TrackListener trackListener;
private ModelListener modelListener;
private SizingListener sizeListener;
private FListener focusListener;
private Rectangle thumbRect = new Rectangle(0, 0, 0, 0);
// Colors
private Color shadowColor;
private Color highlightColor;
private Color focusColor;
public Color getShadowColor() {
return shadowColor;
}
public Color getHighlightColor() {
return highlightColor;
}
public Color getFocusColor() {
return focusColor;
}
/////////////////////////////////////////////////////////////////////////////
// ComponentUI Interface Implementation methods
/////////////////////////////////////////////////////////////////////////////
public static ComponentUI createUI(JComponent b) {
return new BasicSliderUI((JSlider)b);
}
public BasicSliderUI(JSlider b) {
}
public void installUI(JComponent c) {
slider = (JSlider) c;
LookAndFeel.installBorder(slider, "Slider.border");
LookAndFeel.installColors(slider, "Slider.background", "Slider.foreground");
highlightColor = UIManager.getColor("Slider.highlight");
shadowColor = UIManager.getColor("Slider.shadow");
focusColor = UIManager.getColor("Slider.focus");
isDragging = false;
trackListener = new TrackListener();
modelListener = new ModelListener();
sizeListener = new SizingListener();
focusListener = new FListener();
scrollListener = new ScrollListener();
scrollTimer = new Timer(100, scrollListener);
scrollTimer.setInitialDelay(300);
slider.addMouseListener(trackListener);
slider.addMouseMotionListener(trackListener);
slider.addFocusListener(focusListener);
slider.addComponentListener(sizeListener);
slider.addPropertyChangeListener(this);
slider.getModel().addChangeListener(modelListener);
slider.setEnabled(slider.isEnabled());
slider.setOpaque(true);
// I calculate the thumb bounds twice here because the recalc* methods need to know
// the dimensions of the thumb and then to get the final correct thumb bounds, I need
// to calculate again after the recalc* methods have been called. This is obviously
// rediculous, but I don't have the time to change the way that everything is calculated.
calculateThumbBounds();
recalcLabelRect();
recalcTrackBuffer();
calculateThumbBounds();
slider.registerKeyboardAction(new ActionScroller(slider, POSITIVE_SCROLL, false),
KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,0),
JComponent.WHEN_FOCUSED);
slider.registerKeyboardAction(new ActionScroller(slider, NEGATIVE_SCROLL, false),
KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,0),
JComponent.WHEN_FOCUSED);
slider.registerKeyboardAction(new ActionScroller(slider, NEGATIVE_SCROLL, true),
KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN,0),
JComponent.WHEN_FOCUSED);
slider.registerKeyboardAction(new ActionScroller(slider, NEGATIVE_SCROLL, false),
KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,0),
JComponent.WHEN_FOCUSED);
slider.registerKeyboardAction(new ActionScroller(slider, POSITIVE_SCROLL, false),
KeyStroke.getKeyStroke(KeyEvent.VK_UP,0),
JComponent.WHEN_FOCUSED);
slider.registerKeyboardAction(new ActionScroller(slider, POSITIVE_SCROLL, true),
KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP,0),
JComponent.WHEN_FOCUSED);
slider.registerKeyboardAction(new ActionScroller(slider, MIN_SCROLL, true),
KeyStroke.getKeyStroke(KeyEvent.VK_HOME,0),
JComponent.WHEN_FOCUSED);
slider.registerKeyboardAction(new ActionScroller(slider, MAX_SCROLL, true),
KeyStroke.getKeyStroke(KeyEvent.VK_END,0),
JComponent.WHEN_FOCUSED);
}
public void uninstallUI(JComponent c) {
if(c != slider)
throw new IllegalComponentStateException(
this + " was asked to deinstall() "
+ c + " when it only knows about "
+ slider + ".");
LookAndFeel.uninstallBorder(slider);
scrollTimer.stop();
scrollTimer = null;
slider.getModel().removeChangeListener(modelListener);
slider.removeMouseListener(trackListener);
slider.removeMouseMotionListener(trackListener);
slider.removeFocusListener(focusListener);
slider.removeComponentListener(sizeListener);
slider.removePropertyChangeListener(this);
slider.resetKeyboardActions();
thumbRect = null;
slider = null;
}
public Dimension getPreferredHorizontalSize() {
return PREFERRED_HORIZONTAL_SIZE;
}
public Dimension getPreferredVerticalSize() {
return PREFERRED_VERTICAL_SIZE;
}
public Dimension getMinimumHorizontalSize() {
return MINIMUM_HORIZONTAL_SIZE;
}
public Dimension getMinimumVerticalSize() {
return MINIMUM_VERTICAL_SIZE;
}
public Dimension getPreferredSize(JComponent c) {
Dimension d;
if (slider.getOrientation() == JSlider.VERTICAL) {
d = new Dimension(getPreferredVerticalSize());
if(slider.getPaintTicks()) {
d.width += getScrollTickRect().width + 1;
}
if ( slider.getPaintLabels() ) {
d.width += getLabelRect().width + 1;
}
} else {
d = new Dimension(getPreferredHorizontalSize());
if(slider.getPaintTicks()) {
d.height += getScrollTickRect().height + 1;
}
if ( slider.getPaintLabels() ) {
d.height += getLabelRect().height + 1;
}
}
d.width += slider.getInsets().left + slider.getInsets().right;
d.height += slider.getInsets().top + slider.getInsets().bottom;
return d;
}
public Dimension getMinimumSize(JComponent c) {
Dimension d;
if (slider.getOrientation() == JSlider.VERTICAL) {
d = new Dimension(getMinimumVerticalSize());
if(slider.getPaintTicks()) {
d.width += getTickSpace() + 1;
}
if ( slider.getPaintLabels() ) {
d.width += getLabelRect().width + 1;
}
} else {
d = new Dimension(getMinimumHorizontalSize());
if(slider.getPaintTicks()) {
d.height += getTickSpace() + 1;
}
if ( slider.getPaintLabels() ) {
d.height += getLabelRect().height + 1;
}
}
d.width += slider.getInsets().left + slider.getInsets().right;
d.height += slider.getInsets().top + slider.getInsets().bottom;
return d;
}
public Dimension getMaximumSize(JComponent c) {
Dimension d = getPreferredSize(c);
if (slider.getOrientation() == JSlider.VERTICAL) {
d.height = Short.MAX_VALUE;
} else {
d.width = Short.MAX_VALUE;
}
return d;
}
protected Rectangle getFullContentArea() {
Rectangle r = new Rectangle();
Insets insets = slider.getInsets();
Dimension size = slider.getSize();
r.x = insets.left;
r.y = insets.top;
r.width = size.width - (insets.left + insets.right);
r.height = size.height - (insets.top + insets.bottom);
return r;
}
public Rectangle getScrollTrackRect() {
Rectangle r = getFullContentArea();
if ( slider.getPaintTicks() || slider.getPaintLabels() ) {
if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
r.setSize( r.width, (getScrollTickRect().y - 1) - r.y );
}
else {
r.setSize( (getScrollTickRect().x - 1) - r.x, r.height );
}
}
return r;
}
public Rectangle getScrollTickRect() {
Rectangle r = getFullContentArea();
if(slider.getPaintTicks()) {
Rectangle labelRect = getLabelRect();
if(slider.getOrientation() == JSlider.VERTICAL) {
if ( slider.getPaintLabels() ) {
r.setLocation( (labelRect.x - 1) - getTickSize(), r.y );
}
else {
r.setLocation((r.x + (r.width - 1)) - getTickSize(), r.y);
}
r.setSize(getTickSize(), r.height);
} else {
if ( slider.getPaintLabels() ) {
r.setLocation( r.x, (labelRect.y - 1) - getTickSize() );
}
else {
r.setLocation(r.x, (r.y + (r.height - 1)) - getTickSize());
}
r.setSize(r.width, getTickSize());
}
} else {
r.setLocation( labelRect.x, labelRect.y );
if(slider.getOrientation() == JSlider.VERTICAL) {
r.setSize( 0, r.height );
}
else {
r.setSize( r.width, 0 );
}
}
return r;
}
public Rectangle getLabelRect() {
return labelRect;
}
protected void recalcLabelRect() {
Rectangle interior = getFullContentArea();
if ( slider.getPaintLabels() ) {
if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
int maxLabelHeight = getHeightOfTallestLabel();
interior.y = (interior.y + (interior.height - 1)) - maxLabelHeight;
interior.height = maxLabelHeight;
}
else {
int maxLabelWidth = getWidthOfWidestLabel();
interior.x = (interior.x + (interior.width - 1)) - maxLabelWidth;
interior.width = maxLabelWidth;
}
}
else {
if(slider.getOrientation() == JSlider.VERTICAL) {
interior.setLocation( interior.x + (interior.width - 1), interior.y );
interior.setSize( 0, interior.height );
}
else {
interior.setLocation( interior.x, interior.y + (interior.height - 1) );
interior.setSize( interior.width, 0 );
}
}
labelRect = interior;
}
public void propertyChange( PropertyChangeEvent evt ) {
if ( evt.getPropertyName().equals( "labelTable" ) ) {
recalcLabelRect();
recalcTrackBuffer();
}
}
protected void recalcTrackBuffer() {
if ( slider.getPaintLabels() && slider.getLabelTable() != null ) {
Component highLabel = getHighestValueLabel();
Component lowLabel = getLowestValueLabel();
if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
trackBuffer = Math.max( highLabel.getBounds().width, lowLabel.getBounds().width ) / 2;
trackBuffer = Math.max( trackBuffer, getThumbRect().width / 2 );
}
else {
trackBuffer = Math.max( highLabel.getBounds().height, lowLabel.getBounds().height ) / 2;
trackBuffer = Math.max( trackBuffer, getThumbRect().height / 2 );
}
}
else {
if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
trackBuffer = getThumbRect().width / 2;
}
else {
trackBuffer = getThumbRect().height / 2;
}
}
}
protected int getWidthOfWidestLabel() {
Dictionary dictionary = slider.getLabelTable();
int widest = 0;
if ( dictionary != null ) {
Enumeration keys = dictionary.keys();
while ( keys.hasMoreElements() ) {
Component label = (Component)dictionary.get( keys.nextElement() );
widest = Math.max( label.getPreferredSize().width, widest );
}
}
return widest;
}
protected int getHeightOfTallestLabel() {
Dictionary dictionary = slider.getLabelTable();
int tallest = 0;
if ( dictionary != null ) {
Enumeration keys = dictionary.keys();
while ( keys.hasMoreElements() ) {
Component label = (Component)dictionary.get( keys.nextElement() );
tallest = Math.max( label.getPreferredSize().height, tallest );
}
}
return tallest;
}
protected int getWidthOfHighValueLabel() {
Component label = getHighestValueLabel();
int width = 0;
if ( label != null ) {
width = label.getPreferredSize().width;
}
return width;
}
protected int getWidthOfLowValueLabel() {
Component label = getLowestValueLabel();
int width = 0;
if ( label != null ) {
width = label.getPreferredSize().width;
}
return width;
}
protected int getHeightOfHighValueLabel() {
Component label = getHighestValueLabel();
int height = 0;
if ( label != null ) {
height = label.getPreferredSize().height;
}
return height;
}
protected int getHeightOfLowValueLabel() {
Component label = getLowestValueLabel();
int height = 0;
if ( label != null ) {
height = label.getPreferredSize().height;
}
return height;
}
/**
* Returns the label that corresponds to the highest slider value in the label table.
* @see JSlider#setLabelTable
*/
protected Component getLowestValueLabel() {
Dictionary dictionary = slider.getLabelTable();
Component label = null;
if ( dictionary != null ) {
Enumeration keys = dictionary.keys();
if ( keys.hasMoreElements() ) {
int lowestValue = ((Integer)keys.nextElement()).intValue();
while ( keys.hasMoreElements() ) {
int value = ((Integer)keys.nextElement()).intValue();
lowestValue = Math.min( value, lowestValue );
}
label = (Component)dictionary.get( new Integer( lowestValue ) );
}
}
return label;
}
/**
* Returns the label that corresponds to the lowest slider value in the label table.
* @see JSlider#setLabelTable
*/
protected Component getHighestValueLabel() {
Dictionary dictionary = slider.getLabelTable();
Component label = null;
if ( dictionary != null ) {
Enumeration keys = dictionary.keys();
if ( keys.hasMoreElements() ) {
int highestValue = ((Integer)keys.nextElement()).intValue();
while ( keys.hasMoreElements() ) {
int value = ((Integer)keys.nextElement()).intValue();
highestValue = Math.max( value, highestValue );
}
label = (Component)dictionary.get( new Integer( highestValue ) );
}
}
return label;
}
public int getTickSpace() {
return TICK_SPACE;
}
public int getTickSize() {
return getTickSpace();
}
public void paint(Graphics g, JComponent c) {
paintTrack(g);
if ( slider.getPaintTicks() ) {
paintTicks(g);
}
if ( slider.getPaintLabels() ) {
paintLabels(g);
}
paintFocus(g);
paintThumb(g);
}
public void paintFocus(Graphics g) {
if (slider.hasFocus()) {
/// PENDING(klobad) copied from Button code - fix when 2D available
// Draw each dash of the line pixel by pixel.
// The performance of this is surely poor -- Be sure
// to rewrite when 2d graphics package is ready.
Rectangle r = slider.getBounds();
r.x = 0;
r.y = 0;
if(slider.getBorder() != null) {
r = getFullContentArea();
}
g.setColor(getFocusColor());
BasicGraphicsUtils.drawDashedRect(g, r.x + 1, r.y + 1, r.width - 2, r.height - 2);
}
}
public void paintTrack(Graphics g) {
int cx, cy, cw, ch;
int pad;
Rectangle trackBounds = getScrollTrackRect();
if(slider.getOrientation() == JSlider.HORIZONTAL) {
pad = trackBuffer;
cx = pad;
cy = (trackBounds.height / 2) - 2;
cw = trackBounds.width - (trackBuffer * 2);
g.translate(trackBounds.x + cx, trackBounds.y + cy);
g.setColor(getShadowColor());
g.drawLine(0, 0, cw - 1, 0);
g.drawLine(0, 1, 0, 2);
g.setColor(getHighlightColor());
g.drawLine(0, 3, cw, 3);
g.drawLine(cw, 0, cw, 3);
g.setColor(Color.black);
g.drawLine(1, 1, cw-2, 1);
} else {
pad = trackBuffer;
cx = (trackBounds.width / 2) - 2;
cy = pad;
ch = trackBounds.height - (trackBuffer * 2);
g.translate(trackBounds.x + cx, trackBounds.y + cy);
g.setColor(getShadowColor());
g.drawLine(0, 0, 0, ch - 1);
g.drawLine(1, 0, 2, 0);
g.setColor(getHighlightColor());
g.drawLine(3, 0, 3, ch);
g.drawLine(0, ch, 3, ch);
g.setColor(Color.black);
g.drawLine(1, 1, 1, ch-2);
}
g.translate(-(trackBounds.x + cx), -(trackBounds.y + cy));
}
public void paintTicks(Graphics g) {
Rectangle tickBounds = getScrollTickRect();
int i;
int maj, min, max;
int w = tickBounds.width;
int h = tickBounds.height;
int centerEffect, tickHeight;
g.translate(tickBounds.x, tickBounds.y);
g.setColor(slider.getBackground());
g.fillRect(0, 0, tickBounds.width, tickBounds.height);
g.setColor(Color.black);
maj = slider.getMajorTickSpacing();
min = slider.getMinorTickSpacing();
if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
int value = slider.getMinimum();
int xPos = 0;
if ( slider.getMinorTickSpacing() > 0 ) {
while ( value <= slider.getMaximum() ) {
xPos = xPositionForValue( value );
paintMinorTickForHorizSlider( g, tickBounds, xPos );
value += slider.getMinorTickSpacing();
}
}
if ( slider.getMajorTickSpacing() > 0 ) {
value = slider.getMinimum();
while ( value <= slider.getMaximum() ) {
xPos = xPositionForValue( value );
paintMajorTickForHorizSlider( g, tickBounds, xPos );
value += slider.getMajorTickSpacing();
}
}
}
else {
int value = slider.getMinimum();
int yPos = 0;
if ( slider.getMinorTickSpacing() > 0 ) {
while ( value <= slider.getMaximum() ) {
yPos = yPositionForValue( value );
paintMinorTickForVertSlider( g, tickBounds, yPos );
value += slider.getMinorTickSpacing();
}
}
if ( slider.getMajorTickSpacing() > 0 ) {
value = slider.getMinimum();
while ( value <= slider.getMaximum() ) {
yPos = yPositionForValue( value );
paintMajorTickForVertSlider( g, tickBounds, yPos );
value += slider.getMajorTickSpacing();
}
}
}
g.translate(-tickBounds.x, -tickBounds.y);
}
protected void paintMinorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) {
g.drawLine( x, 0, x, tickBounds.height / 2 - 1 );
}
protected void paintMajorTickForHorizSlider( Graphics g, Rectangle tickBounds, int x ) {
g.drawLine( x, 0, x, tickBounds.height - 2 );
}
protected void paintMinorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) {
g.drawLine( 0, y, tickBounds.width / 2 - 1, y );
}
protected void paintMajorTickForVertSlider( Graphics g, Rectangle tickBounds, int y ) {
g.drawLine( 0, y, tickBounds.width - 2, y );
}
public void paintLabels( Graphics g ) {
Rectangle labelBounds = getLabelRect();
g.translate( labelBounds.x, labelBounds.y );
Dictionary dictionary = slider.getLabelTable();
if ( dictionary != null ) {
Enumeration keys = dictionary.keys();
while ( keys.hasMoreElements() ) {
Integer key = (Integer)keys.nextElement();
Component label = (Component)dictionary.get( key );
if ( slider.getOrientation() == JSlider.HORIZONTAL ) {
paintHorizontalLabel( g, key.intValue(), label );
}
else {
paintVerticalLabel( g, key.intValue(), label );
}
}
}
g.translate( -labelBounds.x, -labelBounds.y );
}
/**
* Called for every label in the label table. Used to draw the labels for horizontal sliders.
* The graphics have been translated to the label rect already.
* @see JSlider#setLabelTable
*/
protected void paintHorizontalLabel( Graphics g, int value, Component label ) {
int labelCenter = xPositionForValue( value );
int labelLeft = labelCenter - (label.getPreferredSize().width / 2);
g.translate( labelLeft, 0 );
label.paint( g );
g.translate( -labelLeft, 0 );
}
/**
* Called for every label in the label table. Used to draw the labels for vertical sliders.
* The graphics have been translated to the label rect already.
* @see JSlider#setLabelTable
*/
protected void paintVerticalLabel( Graphics g, int value, Component label ) {
int labelCenter = yPositionForValue( value );
int labelTop = labelCenter - (label.getPreferredSize().height / 2);
g.translate( 0, labelTop );
label.paint( g );
g.translate( 0, -labelTop );
}
public void paintThumb(Graphics g) {
Rectangle knobBounds = getThumbRect();
int w = knobBounds.width;
int h = knobBounds.height;
g.translate(knobBounds.x, knobBounds.y);
if(slider.isEnabled()) {
g.setColor(slider.getBackground());
} else {
g.setColor(slider.getBackground().darker());
}
if(!slider.getPaintTicks()) {
g.fillRect(0, 0, w, h);
g.setColor(Color.black);
g.drawLine(0, h-1, w-1, h-1);
g.drawLine(w-1, 0, w-1, h-1);
g.setColor(BasicGraphicsUtils.controlHighlight);
g.drawLine(0, 0, 0, h-2);
g.drawLine(1, 0, w-2, 0);
g.setColor(BasicGraphicsUtils.controlShadow);
g.drawLine(1, h-2, w-2, h-2);
g.drawLine(w-2, 1, w-2, h-3);
} else if (slider.getOrientation() == JSlider.HORIZONTAL ) {
int cw = w / 2;
g.fillRect(1, 1, w-3, h-1-cw);
Polygon p = new Polygon();
p.addPoint(1, h-cw);
p.addPoint(cw-1, h-1);
p.addPoint(w-2, h-1-cw);
g.fillPolygon(p);
g.setColor(BasicGraphicsUtils.controlHighlight);
g.drawLine(0, 0, w-2, 0);
g.drawLine(0, 1, 0, h-1-cw);
g.drawLine(0, h-cw, cw-1, h-1);
g.setColor(Color.black);
g.drawLine(w-1, 0, w-1, h-2-cw);
g.drawLine(w-1, h-1-cw, w-1-cw, h-1);
g.setColor(BasicGraphicsUtils.controlShadow);
g.drawLine(w-2, 1, w-2, h-2-cw);
g.drawLine(w-2, h-1-cw, w-1-cw, h-2);
} else {
int cw = h / 2;
g.fillRect(1, 1, w-1-cw, h-3);
Polygon p = new Polygon();
p.addPoint(w-cw-1, 0);
p.addPoint(w-1, cw);
p.addPoint(w-1-cw, h-2);
g.fillPolygon(p);
g.setColor(BasicGraphicsUtils.controlHighlight);
g.drawLine(0, 0, 0, h - 2);
g.drawLine(1, 0, w-1-cw, 0);
g.drawLine(w-cw-1, 0, w-1, cw);
g.setColor(Color.black);
g.drawLine(0, h-1, w-2-cw, h-1);
g.drawLine(w-1-cw, h-1, w-1, h-1-cw);
g.setColor(BasicGraphicsUtils.controlShadow);
g.drawLine(1, h-2, w-2-cw, h-2 );
g.drawLine(w-1-cw, h-2, w-2, h-cw-1 );
}
g.translate(-knobBounds.x, -knobBounds.y);
}
public void setThumbBounds(int x, int y, int width, int height) {
Rectangle r = new Rectangle(thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height);
Rectangle r2;
thumbRect.setBounds(x, y, width, height);
r2 = r.union(thumbRect);
slider.repaint(r2.x, r2.y, r2.width, r2.height);
}
public void setThumbLocation(int x, int y) {
Rectangle r = new Rectangle(thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height);
Rectangle r2;
thumbRect.setLocation(x, y);
r2 = r.union(thumbRect);
slider.repaint(r2.x, r2.y, r2.width, r2.height);
}
public Rectangle getThumbRect() {
return thumbRect;
}
public void scrollByBlock(int direction) {
synchronized(slider) {
int oldValue = slider.getValue();
int blockIncrement = slider.getMaximum() / 10;
int delta = blockIncrement * ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);
slider.setValue(oldValue + delta);
}
}
public void scrollByUnit(int direction) {
synchronized(slider) {
int oldValue = slider.getValue();
int delta = 1 * ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);
slider.setValue(oldValue + delta);
}
}
/**
* This function is called when a mousePressed was detected in the track, not
* in the thumb. The default behavior is to scroll by block. You can
* override this method to stop it from scrolling or to add additional behavior.
*/
protected void scrollDueToClickInTrack( int dir ) {
scrollByBlock( dir );
}
protected int xPositionForValue(int randomValue) {
int min = slider.getMinimum();
int max = slider.getMaximum();
Rectangle trackRect = getScrollTrackRect();
int trackLength = trackRect.width - (trackBuffer * 2);
int valueRange = slider.getMaximum() - slider.getMinimum();
double pixelsPerValue = (double)trackLength / (double)valueRange;
int trackLeft = trackRect.x + trackBuffer;
int trackRight = (trackRect.x + (trackRect.width - 1)) - trackBuffer;
int xPosition;
if ( !slider.getInverted() ) {
xPosition = trackLeft;
xPosition += Math.round( pixelsPerValue * (double)(randomValue - min) );
}
else {
xPosition = trackRight;
xPosition -= Math.round( pixelsPerValue * (double)(randomValue - min) );
}
xPosition = Math.max( trackLeft, xPosition );
xPosition = Math.min( trackRight, xPosition );
return xPosition;
}
protected int yPositionForValue(int randomValue) {
int min = slider.getMinimum();
int max = slider.getMaximum();
Rectangle trackRect = getScrollTrackRect();
int trackLength = trackRect.height - (trackBuffer * 2);
int valueRange = slider.getMaximum() - slider.getMinimum();
double pixelsPerValue = (double)trackLength / (double)valueRange;
int trackTop = trackRect.y + trackBuffer;
int trackBottom = (trackRect.y + (trackRect.height - 1)) - trackBuffer;
int yPosition;
if ( !slider.getInverted() ) {
yPosition = trackTop;
yPosition += Math.round( pixelsPerValue * (double)(max - randomValue ) );
}
else {
yPosition = trackTop;
yPosition += Math.round( pixelsPerValue * (double)(randomValue - min) );
}
yPosition = Math.max( trackTop, yPosition );
yPosition = Math.min( trackBottom, yPosition );
return yPosition;
}
public void calculateThumbBounds() {
if(slider.getOrientation() == JSlider.VERTICAL) {
setThumbBounds(getScrollTrackRect().x, yPositionForValue(slider.getValue()) - getThumbRect().height / 2,
getScrollTrackRect().width, 11);
} else {
setThumbBounds(xPositionForValue(slider.getValue()) - getThumbRect().width / 2,
getScrollTrackRect().y,
11, getScrollTrackRect().height);
}
}
/////////////////////////////////////////////////////////////////////////
/// Model Listener Class
/////////////////////////////////////////////////////////////////////////
/**
* Data model listener.
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*/
protected class ModelListener implements ChangeListener,Serializable {
public void stateChanged(ChangeEvent e) {
if(!isDragging)
calculateThumbBounds();
}
}
/////////////////////////////////////////////////////////////////////////
/// Track Listener Class
/////////////////////////////////////////////////////////////////////////
/**
* Track mouse movements.
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*/
protected class TrackListener extends MouseAdapter
implements MouseMotionListener, Serializable {
protected transient int offset;
protected transient int currentMouseX, currentMouseY;
public void mouseReleased(MouseEvent e) {
if(!slider.isEnabled())
return;
offset = 0;
scrollTimer.stop();
// This is the way we have to determine snap-to-ticks. It's hard to explain
// but since ChangeEvents don't give us any idea what has changed we don't
// have a way to stop the thumb bounds from being recalculated. Recalculating
// the thumb bounds moves the thumb over the current value (i.e., snapping
// to the ticks).
if ( slider.getSnapToTicks() ) {
isDragging = false;
slider.setValueIsAdjusting(false);
}
else {
slider.setValueIsAdjusting(false);
isDragging = false;
}
}
/**
* If the mouse is pressed above the "thumb" component
* then reduce the scrollbars value by one page ("page up"),
* otherwise increase it by one page. If there is no
* thumb then page up if the mouse is in the upper half
* of the track.
*/
public void mousePressed(MouseEvent e) {
if(!slider.isEnabled())
return;
currentMouseX = e.getX();
currentMouseY = e.getY();
slider.requestFocus();
// Clicked in the Thumb area?
if(getThumbRect().contains(currentMouseX, currentMouseY)) {
switch (slider.getOrientation()) {
case JSlider.VERTICAL:
offset = currentMouseY - getThumbRect().y;
break;
case JSlider.HORIZONTAL:
offset = currentMouseX - getThumbRect().x;
break;
}
isDragging = true;
slider.setValueIsAdjusting(true);
return;
}
isDragging = false;
slider.setValueIsAdjusting(true);
Dimension sbSize = slider.getSize();
int direction = POSITIVE_SCROLL;
switch (slider.getOrientation()) {
case JSlider.VERTICAL:
if (getThumbRect().isEmpty()) {
int scrollbarCenter = sbSize.height / 2;
if ( !slider.getInverted() ) {
direction = (currentMouseY < scrollbarCenter) ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
}
else {
direction = (currentMouseY < scrollbarCenter) ? NEGATIVE_SCROLL : POSITIVE_SCROLL;
}
} else {
int thumbY = getThumbRect().y;
if ( !slider.getInverted() ) {
direction = (currentMouseY < thumbY) ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
}
else {
direction = (currentMouseY < thumbY) ? NEGATIVE_SCROLL : POSITIVE_SCROLL;
}
}
break;
case JSlider.HORIZONTAL:
if (getThumbRect().isEmpty()) {
int scrollbarCenter = sbSize.width / 2;
if ( !slider.getInverted() ) {
direction = (currentMouseX < scrollbarCenter) ? NEGATIVE_SCROLL : POSITIVE_SCROLL;
}
else {
direction = (currentMouseX < scrollbarCenter) ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
}
} else {
int thumbX = getThumbRect().x;
if ( !slider.getInverted() ) {
direction = (currentMouseX < thumbX) ? NEGATIVE_SCROLL : POSITIVE_SCROLL;
}
else {
direction = (currentMouseX < thumbX) ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
}
}
break;
}
scrollDueToClickInTrack(direction);
Rectangle r = getThumbRect();
if(!r.contains(currentMouseX, currentMouseY)) {
if(shouldScroll(direction)) {
scrollTimer.stop();
scrollListener.setDirection(direction);
scrollTimer.start();
}
}
}
public boolean shouldScroll(int direction) {
Rectangle r = getThumbRect();
if(slider.getOrientation() == JSlider.VERTICAL) {
if(slider.getInverted() ? direction < 0 : direction > 0) {
if(r.y + r.height <= currentMouseY) {
return false;
}
}
else if(r.y >= currentMouseY) {
return false;
}
} else {
if( slider.getInverted() ? direction < 0 : direction > 0) {
if(r.x + r.width >= currentMouseX) {
return false;
}
} else if(r.x <= currentMouseX) {
return false;
}
}
if(direction > 0 && slider.getValue() + slider.getExtent() >= slider.getMaximum()) {
return false;
}
else if(direction < 0 && slider.getValue() <= slider.getMinimum()) {
return false;
}
return true;
}
/**
* Set the models value to the position of the top/left
* of the thumb relative to the origin of the track.
*/
public void mouseDragged(MouseEvent e) {
BasicScrollBarUI ui;
int centerEffect;
Rectangle scrollTrackRect = getScrollTrackRect();
int thumbMiddle = 0;
if(!slider.isEnabled())
return;
currentMouseX = e.getX();
currentMouseY = e.getY();
if(!isDragging)
return;
switch (slider.getOrientation()) {
case JSlider.VERTICAL:
int halfThumbHeight = getThumbRect().height / 2;
int thumbTop = e.getY() - offset;
int trackTop = scrollTrackRect.y + trackBuffer;
int trackBottom = (scrollTrackRect.y + (scrollTrackRect.height - 1)) - trackBuffer;
thumbTop = Math.max( thumbTop, trackTop - halfThumbHeight );
thumbTop = Math.min( thumbTop, trackBottom - halfThumbHeight );
setThumbLocation(getThumbRect().x, thumbTop);
thumbMiddle = thumbTop + halfThumbHeight;
slider.setValue( valueForYPosition( thumbMiddle ) );
break;
case JSlider.HORIZONTAL:
int halfThumbWidth = getThumbRect().width / 2;
int thumbLeft = e.getX() - offset;
int trackLeft = scrollTrackRect.x + trackBuffer;
int trackRight = (scrollTrackRect.x + (scrollTrackRect.width - 1)) - trackBuffer;
thumbLeft = Math.max( thumbLeft, trackLeft - halfThumbWidth );
thumbLeft = Math.min( thumbLeft, trackRight - halfThumbWidth );
setThumbLocation( thumbLeft, getThumbRect().y);
thumbMiddle = thumbLeft + halfThumbWidth;
slider.setValue( valueForXPosition( thumbMiddle ) );
break;
default:
return;
}
}
public void mouseMoved(MouseEvent e) {}
/**
* Returns a value give a y position. If yPos is past the track at the top or the
* bottom it will set the value to the min or max of the slider, depending if the
* slider is inverted or not.
*/
public int valueForYPosition( int yPos ) {
int value;
final int minValue = slider.getMinimum();
final int maxValue = slider.getMaximum();
final Rectangle trackRect = getScrollTrackRect();
final int trackLength = trackRect.height - (trackBuffer * 2);
final int trackTop = trackRect.y + trackBuffer;
final int trackBottom = (trackRect.y + (trackRect.height - 1)) - trackBuffer;
if ( yPos <= trackTop ) {
value = slider.getInverted() ? minValue : maxValue;
}
else if ( yPos >= trackBottom ) {
value = slider.getInverted() ? maxValue : minValue;
}
else {
int distanceFromTrackTop = yPos - trackTop;
int valueRange = maxValue - minValue;
double valuePerPixel = (double)valueRange / (double)trackLength;
int valueFromTrackTop = (int)Math.round( distanceFromTrackTop * valuePerPixel );
value = slider.getInverted() ? minValue + valueFromTrackTop : maxValue - valueFromTrackTop;
}
return value;
}
/**
* Returns a value give an x position. If xPos is past the track at the left or the
* right it will set the value to the min or max of the slider, depending if the
* slider is inverted or not.
*/
public int valueForXPosition( int xPos ) {
int value;
final int minValue = slider.getMinimum();
final int maxValue = slider.getMaximum();
final Rectangle trackRect = getScrollTrackRect();
final int trackLength = trackRect.width - (trackBuffer * 2);
final int trackLeft = trackRect.x + trackBuffer;
final int trackRight = (trackRect.x + (trackRect.width - 1)) - trackBuffer;
if ( xPos <= trackLeft ) {
value = slider.getInverted() ? maxValue : minValue;
}
else if ( xPos >= trackRight ) {
value = slider.getInverted() ? minValue : maxValue;
}
else {
int distanceFromTrackLeft = xPos - trackLeft;
int valueRange = maxValue - minValue;
double valuePerPixel = (double)valueRange / (double)trackLength;
int valueFromTrackLeft = (int)Math.round( distanceFromTrackLeft * valuePerPixel );
value = slider.getInverted() ? maxValue - valueFromTrackLeft :
minValue + valueFromTrackLeft;
}
return value;
}
}
/**
* Scroll-event listener.
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*/
public class ScrollListener implements ActionListener, Serializable
// changed this class to public to avoid bogus IllegalAccessException bug i
// InternetExplorer browser. It was protected. Work around for 4109432
{
int direction = POSITIVE_SCROLL;
boolean useBlockIncrement;
public ScrollListener() {
direction = POSITIVE_SCROLL;
useBlockIncrement = true;
}
public ScrollListener(int dir, boolean block) {
direction = dir;
useBlockIncrement = block;
}
public void setDirection(int direction) { this.direction = direction; }
public void setScrollByBlock(boolean block) { this.useBlockIncrement = block; }
public void actionPerformed(ActionEvent e) {
if(useBlockIncrement) {
scrollByBlock(direction);
}
else {
scrollByUnit(direction);
}
if(!trackListener.shouldScroll(direction)) {
((Timer)e.getSource()).stop();
}
}
};
/// Possible bug in ComponentListener.
// I don't seem to be getting the resize events, only moves.
// recalculate any time, just in case.
/**
* Listener for resizing events.
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*/
protected class SizingListener implements ComponentListener, Serializable {
public void componentResized(ComponentEvent e) {
recalcLabelRect();
calculateThumbBounds();
}
public void componentHidden(ComponentEvent e) {
recalcLabelRect();
calculateThumbBounds();
}
public void componentMoved(ComponentEvent e) {
recalcLabelRect();
calculateThumbBounds();
}
public void componentShown(ComponentEvent e) {
recalcLabelRect();
calculateThumbBounds();
}
};
/**
* Focus-change listener.
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*/
protected class FListener implements FocusListener, Serializable {
public void focusGained(FocusEvent e) { slider.repaint(); }
public void focusLost(FocusEvent e) { slider.repaint(); }
};
/**
* Defines the action to take when scrolled.
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*/
protected class ActionScroller extends AbstractAction implements Serializable
{
int dir;
boolean block;
JSlider slider;
public ActionScroller( JSlider slider, int dir, boolean block) {
this.dir = dir;
this.block = block;
this.slider = slider;
}
public void actionPerformed(ActionEvent e) {
if ( slider.isEnabled() ) {
if(dir == NEGATIVE_SCROLL || dir == POSITIVE_SCROLL) {
int realDir = dir;
if ( slider.getInverted() ) {
realDir = dir == NEGATIVE_SCROLL ? POSITIVE_SCROLL : NEGATIVE_SCROLL;
}
if(block)
scrollByBlock(realDir);
else
scrollByUnit(realDir);
} else {
if ( slider.getInverted() ) {
if(dir == MIN_SCROLL)
slider.setValue(slider.getMaximum());
else if(dir == MAX_SCROLL)
slider.setValue(slider.getMinimum());
}
else {
if(dir == MIN_SCROLL)
slider.setValue(slider.getMinimum());
else if(dir == MAX_SCROLL)
slider.setValue(slider.getMaximum());
}
}
}
}
public boolean isEnabled() { return true; }
};
}